using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

[assembly: IsolatedStorageFilePermission(SecurityAction.RequestMinimum, UsageAllowed = IsolatedStorageContainment.DomainIsolationByUser)]


namespace DbVisualizer.DbConnection.MRU
{
    [Serializable]
    public class MruList : IEnumerable<string>
    {
        private const int MaxItems = 10;
        private readonly string mConnectionString;
        private readonly List<string> mList = new List<string>();

        /// <summary>
        ///     Default constructor
        /// </summary>
        /// <param name="connectionString">The connection string to use. Must not be null or empty</param>
        /// <remarks>Private as this object can only be created using one of the shared methods.</remarks>
        private MruList(string connectionString)
        {
            // Connection string must be set to a value
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new ArgumentNullException("connectionString");
            }

            // Set the properties
            mConnectionString = connectionString;
        }

        /// <summary>
        ///     Returns/Sets the connection string
        /// </summary>
        /// <remarks>This connection string is used as a key in the list of mru lists</remarks>
        private string ConnectionString
        {
            get { return mConnectionString; }
        }

        /// <summary>
        ///     Returns the enumerator for this list
        /// </summary>
        public IEnumerator<string> GetEnumerator()
        {
            return mList.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///     Gets an MRU List for a particular connection string
        /// </summary>
        /// <param name="connectionString">The connection string to get the MRU List for</param>
        /// <returns>A list of recently executed queries for a connection string. An empty list if none exists.</returns>
        public static MruList GetMruList(string connectionString)
        {
            // Local variables
            MruList list;

            // Get all the MRU Lists from the persistable storage 
            var lists = GetMruLists();

            // Get the list if it exists

            if (lists.ContainsKey(connectionString))
            {
                list = lists[connectionString];
            }
            else
            {
                // simply return an empty one
                list = new MruList(connectionString);
                lists.Add(list.ConnectionString, list);
            }

            // return the list
            return list;
        }

        /// <summary>
        ///     Returns all the MRU lists from the persistance store
        /// </summary>
        /// <returns>All the lists</returns>
        private static Dictionary<string, MruList> GetMruLists()
        {
            // Local Variables
            Dictionary<string, MruList> lists;

            try
            {
                // Use an isolated storage file to get the lists from
                using (var file = IsolatedStorageFile.GetUserStoreForDomain())
                {
                    // Get the filestream from the ISFile
                    using (
                        var stream = new IsolatedStorageFileStream("DBVisualizer.dat", FileMode.Open, FileAccess.Read, file)
                        )
                    {
                        // Get the serialization formatter
                        var formatter = new BinaryFormatter();

                        // deserialize the lists
                        lists = (Dictionary<string, MruList>)formatter.Deserialize(stream);
                    }
                }
            }
            catch (Exception)
            {
                // Return an empty list
                lists = new Dictionary<string, MruList>();
            }

            // Return the lists
            return lists;
        }


        /// <summary>
        ///     Saves an MRU list to persistable stoage
        /// </summary>
        /// <param name="list">The list to save</param>
        /// <remarks></remarks>
        private static void SaveList(MruList list)
        {
            // Get the lists into memory
            var lists = GetMruLists();

            // Set the item
            lists[list.ConnectionString] = list;

            // Save the lists back to the files
            SaveMruLists(lists);
        }

        /// <summary>
        ///     Saves all MRU list back to the isolated stoage file
        /// </summary>
        /// <param name="lists">The lists to save</param>
        private static void SaveMruLists(Dictionary<string, MruList> lists)
        {
            // Local Variables

            // Open the Isolated store
            using (var file = IsolatedStorageFile.GetUserStoreForDomain())
            {
                // Open (and overwrite an existing file)
                using (
                    var stream = new IsolatedStorageFileStream("DBVisualizer.dat", FileMode.Create, FileAccess.Write, file))
                {
                    // Create the formatter for serialization purposes
                    var formatter = new BinaryFormatter();

                    // Serialize the lists to binary and get the buffer
                    formatter.Serialize(stream, lists);
                }
            }
        }

        /// <summary>
        ///     Saves the list to the persistable store
        /// </summary>
        public void Save()
        {
            // Call the shared method.
            SaveList(this);
        }


        public void Add(string queryText)
        {
            // exit if it already exists

            if (mList.Contains(queryText))
            {
                return;
            }

            // If we've gone past the last item, remove the first item

            if (mList.Count >= MaxItems)
            {
                TrimList(MaxItems - 1);
            }

            // Add to the list
            mList.Add(queryText);
        }

        /// <summary>
        ///     Removes items from the beginning of the list until it holds the max items
        /// </summary>
        /// <param name="maxItems">The num of max items</param>
        private void TrimList(int maxItems)
        {
            // Local Variables
            var currentCount = mList.Count;

            for (var index = 0; index <= currentCount - maxItems; index++)
            {
                mList.RemoveAt(index);
            }
        }
    }
}